﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BulkCopy.Internals
{
    using System;
    using System.Data;
    using System.Linq;
    using System.Text;
    using System.Globalization;

    internal class CommonFunctions
    {
        /// <summary>
        /// Method to get the column names
        /// </summary>
        /// <param name="mapItemCollection"></param>
        /// <param name="propertyToFetch"></param>
        /// <returns></returns>
        public string GetColumnNames(ColumnMapItemCollection mapItemCollection, DataTable dataTable, ColumnProperty propertyToFetch)
        {
            StringBuilder builder = new StringBuilder();
            if (mapItemCollection == null || mapItemCollection.Count <= 0)
            {
                foreach (DataColumn col in dataTable.Columns)
                {
                    builder.AppendFormat("{0},", col.ColumnName);
                }
            }
            else
            {
                switch (propertyToFetch)
                {
                    case ColumnProperty.Source:
                        foreach (ColumnMapItem columnMapItem in mapItemCollection)
                        {
                            builder.AppendFormat("{0},", columnMapItem.SourceColumn);
                        }
                        break;
                    case ColumnProperty.Destination:
                        foreach (ColumnMapItem columnMapItem in mapItemCollection)
                        {
                            builder.AppendFormat("{0},", columnMapItem.DestinationColumn);
                        }
                        break;
                    default:
                        builder.Append(",");
                        break;
                }
            }
            if (builder.Length > 0)
                builder.Length--;

            return builder.ToString();
        }

        public string ConstructSql(string tableName, DataRow row, ColumnMapItemCollection mapItemCollection)
        {
            // Get the column names that need to be used
            string columnNames = GetColumnNames(mapItemCollection, row.Table, ColumnProperty.Destination);

            // construct the base Skeleton of the sql.
            string baseSql = string.Format("insert into {0}({1}) values({2})", tableName, columnNames, "{0}");

            // loop through the collection and construct the values string
            StringBuilder builder = new StringBuilder();
            if (mapItemCollection == null || mapItemCollection.Count <= 0)
            {
                foreach (DataColumn col in row.Table.Columns)
                {
                    if (col.DataType == typeof(string))
                        builder.AppendFormat("'{0}',", row[col.ColumnName].ToString());
                    else if (col.DataType == typeof(DateTime))
                        builder.AppendFormat("'{0:yyyy-MM-dd HH:mm:ss}',", row[col.ColumnName]);
                    else
                        builder.AppendFormat("{0},", row[col.ColumnName].ToString());
                }
            }
            else
            {
                foreach (ColumnMapItem columnMapItem in mapItemCollection)
                {
                    string constructedValue = ConstructIndividualValue(columnMapItem.type,
                                                                       row[columnMapItem.SourceColumn].ToString());
                    builder.Append(constructedValue);
                }
            }
            if (builder.Length > 0)
                builder.Length--;

            return string.Format(baseSql, builder.ToString());
        }

        /// <summary>
        /// Method that constructs the individual value. This method determines the quote model based on the datatype
        /// </summary>
        /// <param name="dataType">data type of destination column</param>
        /// <param name="value">Value that to be constructed</param>
        /// <returns>formatted value based on data type</returns>
        private string ConstructIndividualValue(DataType dataType, string value)
        {
            string returnValue = "";
            switch (dataType)
            {
                /*case "INT":
                case "TINYINT":
                case "SMALLINT":
                case "MEDIUMINT":
                case "BIGINT":
                case "FLOAT":
                case "DOUBLE":
                case "DECIMAL":*/
                case DataType.Number:
                    if (value.Length == 0)
                    {
                        returnValue = "NULL,";
                    }
                    else
                    {
                        returnValue = string.Format("{0},", value);
                    }
                    break;
                /*case "CHAR":
                case "VARCHAR":
                case "BLOB":
                case "TEXT":
                case "TINYBLOB":
                case "TINYTEXT":
                case "MEDIUMBLOB":
                case "MEDIUMTEXT":
                case "LONGBLOB":
                case "LONGTEXT":
                case "ENUM":*/
                case DataType.Text:
                    returnValue = string.Format("'{0}',", EscapeStringNew(value));
                    break;
                //case "DATE":
                case DataType.Date:
                    returnValue = String.Format("'{0:yyyy-MM-dd}',", value);
                    //returnValue = string.Format(CultureInfo.InvariantCulture, "{0:dd-MM-yyyy}", value);
                    break;
                //case "TIMESTAMP":
                case DataType.DateTime:
                //case "DATETIME":
                    DateTime date = DateTime.Parse(value);
                    returnValue = String.Format("'{0:yyyy-MM-dd HH:mm:ss}',", date);
                    break;
                case DataType.Time:
                //case "TIME":
                    returnValue = String.Format("'{0:HH:mm:ss}',", value);
                    break;
                case DataType.Year_2:
                //case "YEAR2":
                    returnValue = String.Format("'{0:yy}',", value);
                    break;
                //case "YEAR4":
                case DataType.Year_4:
                    returnValue = String.Format("'{0:yyyy}',", value);
                    break;
                default:
                    // we don't understand the format. to safegaurd the code, just enclose with ''
                    returnValue = string.Format("'{0}',", EscapeStringNew(value));
                    break;
            }
            return returnValue;
        }


        public static string EscapeStringNew(string value)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < value.Length; i++)
            {
                if (value[i] == '\\'
                    || value[i] == '\''
                    || value[i] == '\"'
                    || value[i] == '`'
                    || value[i] == '′'
                    || value[i] == '’'
                    || value[i] == '‘')
                    sb.Append("\\");
                sb.Append(value[i]);
            }
            return sb.ToString();
        }

    }
}
